import {
	Viewer,
	PolygonGeometry,
	PolygonHierarchy,
	WallGeometry,
	GeometryInstance,
	Cartesian3,
	Matrix4,
	MaterialAppearance,
	PolylineMaterialAppearance,
	Material,
	Primitive,
	ColorGeometryInstanceAttribute,
	Color,
	ShowGeometryInstanceAttribute,
	PolylineGeometry,
	ArcType,
	VertexFormat,
	Appearance,
	Matrix3,
	Math
} from 'cesium'
class RangeBlock {

	viewer : Viewer | null
	isLoad : boolean = false

	wallPrimitive : any
	wallLinePrimitive : any

	extrudeHeight : number = 25000

	constructor(viewer : Viewer) {
		this.viewer = viewer
	}

	async load(code : string) {
		if (this.isLoad) return
		this.isLoad = true

		let result = await import(`../../../../static/gisData/Provinces.json`)
		if (result == null) return
		let features = result.features.filter((feature : any) => {
			return feature.properties.CODE == code
		})

		const lonlats = this.parseGeojson({ type: 'FeatureCollection', features })

		this.createWallPrimitive(this.viewer, lonlats)
		this.createWalllinePrimitive(this.viewer, lonlats)
	}

	//将geojson解析成坐标数组
	parseGeojson(geojson : any) {
		const extrudeHeight = this.extrudeHeight
		if (geojson == null) return []
		if (geojson.type == 'FeatureCollection') {
			return parseFeatureC(geojson)
		}
		else if (geojson.type == 'Feature') {
			return parseFeacture(geojson)
		}

		function parseFeatureC(geojson : any) {
			if (geojson.type == 'FeatureCollection') {
				return geojson.features.map((item : any) => { return parseFeacture(item) })[0]
			}
			else return null
		}

		function parseFeacture(geojson : any) {
			if (geojson.type != 'Feature') return null
			if (geojson.geometry == null) return null
			const geometry = geojson.geometry
			return parseGeometry(geometry)
		}

		function parseGeometry(geometry : any) {
			if (geometry.type == 'MultiPolygon') {
				const coordinates = geometry.coordinates[0][0]
				let lonlats = []
				coordinates.forEach((item) => { lonlats = lonlats.concat([...item, extrudeHeight]) })
				return lonlats
			}
			else if (geometry.type == 'Polygon') {
				const coordinates = geometry.coordinates[0]
				let lonlats = []
				coordinates.forEach((item) => { lonlats = lonlats.concat([...item, extrudeHeight]) })
				return lonlats
			}
			else return null
		}
	}

	//创建墙体几何体
	createWallPrimitive(viewer : Viewer, coordinates : Array<number>) {
		const color = [102, 184, 217, 0.2];
		const extrudeWall = new WallGeometry({
			positions: Cartesian3.fromDegreesArrayHeights(coordinates),
			//maximumHeights: coordinates.map(()=>{return 5000}),
			//minimumHeights: coordinates.map(()=>{return 0}),
		})
		const geometryInstance = new GeometryInstance({
			geometry: extrudeWall,
			id: 'rangeBlock-instance',
			modelMatrix: Matrix4.IDENTITY.clone(),
		})
		const appearance = new MaterialAppearance({
			translucent: true,
			closed: true,
			vertexShaderSource: `
			in vec3 position3DHigh;
			in vec3 position3DLow;
			in vec3 normal;
			in vec2 st;
			in float batchId;
			
			out vec3 v_positionEC;
			out vec3 v_positionMC;
			out vec3 v_normalEC;
			out vec2 v_st;
			
			void main()
			{
			    vec4 p = czm_computePosition();
			
			    v_positionEC = (czm_modelViewRelativeToEye * p).xyz;      // position in eye coordinates
			    v_positionMC = (czm_inverseModel * vec4(p.xyz, 1.0)).xyz;
			    v_normalEC = czm_normal * normal;                         // normal in eye coordinates
			    v_st = st;
			
			    vec4 positionWC = czm_modelViewProjectionRelativeToEye * p;
			    gl_Position = positionWC;
			}
			`,
			fragmentShaderSource: `
			    in vec3 v_positionEC;
				in vec3 v_positionMC;
			    in vec3 v_normalEC;
			    in vec2 v_st;
				uniform float time;
			    uniform float grow;
				
			    void main()
			    {
			        vec3 positionToEyeEC = -v_positionEC;
			        vec3 normalEC = normalize(v_normalEC);
					float height = clamp(v_positionMC.y/5000.0,0.0,1.0);
					float scrollH = 0.9;
			        czm_materialInput materialInput;
			        czm_material material = czm_getMaterial(materialInput);
			        
			        // 使用 Appearance 中的 uniform customColor 来改变颜色
			        material.diffuse  = vec3(${color[0]}.0/255.0,${color[1]}.0/255.0,${color[2]}.0/255.0); 
					material.alpha = ${color[3]};
						if(time - scrollH > 0.0 && v_st.t > time - scrollH && time > v_st.t){
							float offset = 0.0;
							offset = 1.0 - (time - v_st.t)/scrollH;
							offset = cos((0.5 - offset)/0.5*3.14/2.0);//(0.5 - abs(0.5 - offset))/0.5;
							offset = clamp(offset, 0.2, 0.6);
							material.diffuse =  vec3(${color[0]}.0/255.0,${color[1]}.0/255.0,${color[2]}.0/255.0 + offset); 
							material.alpha = offset;
						}
						else if (time - scrollH <= 0.0 && (v_st.t > 1.0 - (scrollH - time) || v_st.t < time)){
							float offset = 0.0;
							if(v_st.t < time)
							   offset = 1.0 - (time - v_st.t)/scrollH;
							else if(v_st.t > time) 
							   offset = (v_st.t - (1.0 + time - scrollH))/scrollH;
							offset = cos((0.5 - offset)/0.5*3.14/2.0);//(0.5 - abs(0.5 - offset))/0.5;
							material.diffuse =  vec3(${color[0]}.0/255.0,${color[1]}.0/255.0,${color[2]}.0/255.0 + offset); 
							offset = clamp(offset, 0.2, 0.6);
							material.alpha = offset;
						}
						//material.alpha = v_st.t;
					if(v_st.t >= grow) material.alpha = 0.0;
			        
			     
			        out_FragColor = vec4(material.diffuse + material.emission, material.alpha);        
			    }
			`
		})
		appearance.uniforms = {
			time: 0.0,
			grow: 0.0
		}

		const primitive = new Primitive({
			geometryInstances: geometryInstance,
			appearance: appearance,
			asynchronous: true,
			releaseGeometryInstances: true,
			compressVertices: true,
			allowPicking: false
		})
		viewer.scene.primitives.add(primitive)
		this.wallPrimitive = primitive

		viewer.scene.preRender.addEventListener(this._renderWallEvent.bind(appearance))

		this.countdownFrameRender(1, (rate : number) => {
			appearance.uniforms.grow = rate
		})
	}

	_renderWallEvent() {
		let newTime = Date.now()
		let interval = newTime % 5000 / 5000

		this.uniforms.time = interval
	}

	//创建墙线几何体
	createWalllinePrimitive(viewer : Viewer, coordinates : Array<number>) {
		const color = [102, 184, 217, 0.8]
		const hColor = [102, 254, 257, '0.8']
		const wallLine = new PolylineGeometry({
			positions: Cartesian3.fromDegreesArrayHeights(coordinates),
			width: 2,
			vertexFormat: VertexFormat.ALL
		})
		const geometryInstance = new GeometryInstance({
			geometry: wallLine,
			id: 'wallLine-instance',
			modelMatrix: Matrix4.IDENTITY.clone(),
		})
		const appearance = new PolylineMaterialAppearance({
			translucent: true,
			material: new Material({
				fabric: {
					uniforms: {
						time: 0,
						specular: new Color(1.0, 1.0, 1.0, 1.0),
						grow: 0
					},
					source: `
					    uniform float time;
						uniform vec4 specular;
						float scrollH = 0.4;
						
						czm_material czm_getMaterial(czm_materialInput materialInput) {
							czm_material material = czm_getDefaultMaterial(materialInput);
							vec2 st = materialInput.st;
							
							material.diffuse = vec3(${color[0]}.0/255.0,${color[1]}.0/255.0,${color[2]}.0/255.0);
						
							if(time - scrollH > 0.0 && st.s > time - scrollH && time > st.s){
								material.diffuse =  vec3(${hColor[0]}.0/255.0,${hColor[1]}.0/255.0,${hColor[2]}.0/255.0); 
								material.alpha = ${hColor[3]};
								material.shininess = 32.0;
								material.emission = material.diffuse;
							}
							else if (time - scrollH <= 0.0 && (st.s > 1.0 - (scrollH - time) || st.s < time)){
								material.diffuse =  vec3(${hColor[0]}.0/255.0,${hColor[1]}.0/255.0,${hColor[2]}.0/255.0); 
								material.alpha = ${hColor[3]};
								material.shininess = 32.0;
								material.emission = material.diffuse;
							}
							material.alpha = material.alpha * grow;
							return material;
						}
					`
				},

			}),

		})

		const primitive = new Primitive({
			geometryInstances: geometryInstance,
			appearance: appearance,
			asynchronous: true,
			releaseGeometryInstances: true,
			compressVertices: true,
			allowPicking: false
		})
		viewer.scene.primitives.add(primitive)
		this.wallLinePrimitive = primitive
		viewer.scene.preRender.addEventListener(this._renderWallLineEvent.bind(appearance.material))
		
		// 加载动画
		this.countdownFrameRender(1, (rate : number) => {
			appearance.material.uniforms.grow = rate
		})
	}

	private _renderWallLineEvent() {
		const intervalTime = 40;
		const newTime = Date.now()
		const interval = newTime % (1000 * intervalTime) / (1000 * intervalTime)

		this.uniforms.time = interval
	}



	/**
	 * 倒计时帧动画
	 * @param {number} time 倒计时时间
	 * @param {Function} callback 渲染帧回调
	 */
	private countdownFrameRender(time : number, callback : CountdownFrameCallback) {
		const viewer = this.viewer
		return new Promise((resolve) => {
			const originalDate = Date.now()
			const frameFunc = () => {
				const dateNow = Date.now()
				const spend = dateNow - originalDate
				if (spend > time * 1000) {
					viewer.scene.preRender.removeEventListener(frameFunc)
					resolve('')
				}
				let rate = spend / (time * 1000)
				if (rate > 1) rate = 1
				callback(rate)
			}
			viewer.scene.preRender.addEventListener(frameFunc)
		})
	}

	async unload() {
		if (this.isLoad == false) return
		this.isLoad = false

		await this.countdownFrameRender(0.5, function (rate : number) {
			this.wallPrimitive && (this.wallPrimitive.appearance.uniforms.grow = 1 - rate)
			this.wallLinePrimitive && (this.wallLinePrimitive.appearance.material.uniforms.grow = 1 - rate)
		}.bind(this))
		
		this.viewer.scene.primitives.remove(this.wallPrimitive)
		this.viewer.scene.preRender.removeEventListener(this._renderWallEvent)
		this.viewer.scene.primitives.remove(this.wallLinePrimitive)
		this.viewer.scene.preRender.removeEventListener(this._renderWallLineEvent)
		//this.wallPrimitive.destroy()
		this.wallPrimitive = null
		//this.wallLinePrimitive.destroy()
		this.wallLinePrimitive = null
	}
}

export default RangeBlock

/**
 * 倒计时帧回调
 */
type CountdownFrameCallback = (rate : number) => void